1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package com.sun.java.swing.plaf.gtk;
26
27 import sun.swing.SwingUtilities2;
28 import com.sun.java.swing.plaf.gtk.GTKConstants.ArrowType;
29 import com.sun.java.swing.plaf.gtk.GTKConstants.ShadowType;
30
31 import javax.swing.plaf.ColorUIResource;
32 import javax.swing.plaf.synth.*;
33
34 import java.awt.*;
35 import java.awt.geom.*;
36 import java.awt.image.*;
37 import java.io.*;
38 import java.net.*;
39 import java.security.*;
40 import java.util.*;
41
42 import javax.swing.*;
43 import javax.swing.border.*;
44
45 import javax.xml.parsers.*;
46 import org.xml.sax.SAXException;
47 import org.w3c.dom.*;
48
49
50
51 class Metacity implements SynthConstants {
52
53
54
55
56
57
58 static Metacity INSTANCE;
59
60 private static final String[] themeNames = {
61 getUserTheme(),
62 "blueprint",
63 "Bluecurve",
64 "Crux",
65 "SwingFallbackTheme"
66 };
67
68 static {
69 for (String themeName : themeNames) {
70 if (themeName != null) {
71 try {
72 INSTANCE = new Metacity(themeName);
73 } catch (FileNotFoundException ex) {
74 } catch (IOException ex) {
75 logError(themeName, ex);
76 } catch (ParserConfigurationException ex) {
77 logError(themeName, ex);
78 } catch (SAXException ex) {
79 logError(themeName, ex);
80 }
81 }
82 if (INSTANCE != null) {
83 break;
84 }
85 }
86 if (INSTANCE == null) {
87 throw new Error("Could not find any installed metacity theme, and fallback failed");
88 }
89 }
90
91 private static boolean errorLogged = false;
92 private static DocumentBuilder documentBuilder;
93 private static Document xmlDoc;
94 private static String userHome;
95
96 private Node frame_style_set;
97 private Map<String, Object> frameGeometry;
98 private Map<String, Map<String, Object>> frameGeometries;
99
100 private LayoutManager titlePaneLayout = new TitlePaneLayout();
101
102 private ColorizeImageFilter imageFilter = new ColorizeImageFilter();
103 private URL themeDir = null;
104 private SynthContext context;
105 private String themeName;
106
107 private ArithmeticExpressionEvaluator aee = new ArithmeticExpressionEvaluator();
108 private Map<String, Integer> variables;
109
110
111 private RoundRectClipShape roundedClipShape;
112
113 protected Metacity(String themeName) throws IOException, ParserConfigurationException, SAXException {
114 this.themeName = themeName;
115 themeDir = getThemeDir(themeName);
116 if (themeDir != null) {
117 URL themeURL = new URL(themeDir, "metacity-theme-1.xml");
118 xmlDoc = getXMLDoc(themeURL);
119 if (xmlDoc == null) {
120 throw new IOException(themeURL.toString());
121 }
122 } else {
123 throw new FileNotFoundException(themeName);
124 }
125
126
127 variables = new HashMap<String, Integer>();
128 NodeList nodes = xmlDoc.getElementsByTagName("constant");
129 int n = nodes.getLength();
130 for (int i = 0; i < n; i++) {
131 Node node = nodes.item(i);
132 String name = getStringAttr(node, "name");
133 if (name != null) {
134 String value = getStringAttr(node, "value");
135 if (value != null) {
136 try {
137 variables.put(name, Integer.parseInt(value));
138 } catch (NumberFormatException ex) {
139 logError(themeName, ex);
140
141 }
142 }
143 }
144 }
145
146
147 frameGeometries = new HashMap<String, Map<String, Object>>();
148 nodes = xmlDoc.getElementsByTagName("frame_geometry");
149 n = nodes.getLength();
150 for (int i = 0; i < n; i++) {
151 Node node = nodes.item(i);
152 String name = getStringAttr(node, "name");
153 if (name != null) {
154 HashMap<String, Object> gm = new HashMap<String, Object>();
155 frameGeometries.put(name, gm);
156
157 String parentGM = getStringAttr(node, "parent");
158 if (parentGM != null) {
159 gm.putAll(frameGeometries.get(parentGM));
160 }
161
162 gm.put("has_title",
163 Boolean.valueOf(getBooleanAttr(node, "has_title", true)));
164 gm.put("rounded_top_left",
165 Boolean.valueOf(getBooleanAttr(node, "rounded_top_left", false)));
166 gm.put("rounded_top_right",
167 Boolean.valueOf(getBooleanAttr(node, "rounded_top_right", false)));
168 gm.put("rounded_bottom_left",
169 Boolean.valueOf(getBooleanAttr(node, "rounded_bottom_left", false)));
170 gm.put("rounded_bottom_right",
171 Boolean.valueOf(getBooleanAttr(node, "rounded_bottom_right", false)));
172
173 NodeList childNodes = node.getChildNodes();
174 int nc = childNodes.getLength();
175 for (int j = 0; j < nc; j++) {
176 Node child = childNodes.item(j);
177 if (child.getNodeType() == Node.ELEMENT_NODE) {
178 name = child.getNodeName();
179 Object value = null;
180 if ("distance".equals(name)) {
181 value = Integer.valueOf(getIntAttr(child, "value", 0));
182 } else if ("border".equals(name)) {
183 value = new Insets(getIntAttr(child, "top", 0),
184 getIntAttr(child, "left", 0),
185 getIntAttr(child, "bottom", 0),
186 getIntAttr(child, "right", 0));
187 } else if ("aspect_ratio".equals(name)) {
188 value = new Float(getFloatAttr(child, "value", 1.0F));
189 } else {
190 logError(themeName, "Unknown Metacity frame geometry value type: "+name);
191 }
192 String childName = getStringAttr(child, "name");
193 if (childName != null && value != null) {
194 gm.put(childName, value);
195 }
196 }
197 }
198 }
199 }
200 frameGeometry = frameGeometries.get("normal");
201 }
202
203
204 public static LayoutManager getTitlePaneLayout() {
205 return INSTANCE.titlePaneLayout;
206 }
207
208 private Shape getRoundedClipShape(int x, int y, int w, int h,
209 int arcw, int arch, int corners) {
210 if (roundedClipShape == null) {
211 roundedClipShape = new RoundRectClipShape();
212 }
213 roundedClipShape.setRoundedRect(x, y, w, h, arcw, arch, corners);
214
215 return roundedClipShape;
216 }
217
218 void paintButtonBackground(SynthContext context, Graphics g, int x, int y, int w, int h) {
219 updateFrameGeometry(context);
220
221 this.context = context;
222 JButton button = (JButton)context.getComponent();
223 String buttonName = button.getName();
224 int buttonState = context.getComponentState();
225
226 JComponent titlePane = (JComponent)button.getParent();
227 Container titlePaneParent = titlePane.getParent();
228
229 JInternalFrame jif;
230 if (titlePaneParent instanceof JInternalFrame) {
231 jif = (JInternalFrame)titlePaneParent;
232 } else if (titlePaneParent instanceof JInternalFrame.JDesktopIcon) {
233 jif = ((JInternalFrame.JDesktopIcon)titlePaneParent).getInternalFrame();
234 } else {
235 return;
236 }
237
238 boolean active = jif.isSelected();
239 button.setOpaque(false);
240
241 String state = "normal";
242 if ((buttonState & PRESSED) != 0) {
243 state = "pressed";
244 } else if ((buttonState & MOUSE_OVER) != 0) {
245 state = "prelight";
246 }
247
248 String function = null;
249 String location = null;
250 boolean left_corner = false;
251 boolean right_corner = false;
252
253
254 if (buttonName == "InternalFrameTitlePane.menuButton") {
255 function = "menu";
256 location = "left_left";
257 left_corner = true;
258 } else if (buttonName == "InternalFrameTitlePane.iconifyButton") {
259 function = "minimize";
260 int nButtons = ((jif.isIconifiable() ? 1 : 0) +
261 (jif.isMaximizable() ? 1 : 0) +
262 (jif.isClosable() ? 1 : 0));
263 right_corner = (nButtons == 1);
264 switch (nButtons) {
265 case 1: location = "right_right"; break;
266 case 2: location = "right_middle"; break;
267 case 3: location = "right_left"; break;
268 }
269 } else if (buttonName == "InternalFrameTitlePane.maximizeButton") {
270 function = "maximize";
271 right_corner = !jif.isClosable();
272 location = jif.isClosable() ? "right_middle" : "right_right";
273 } else if (buttonName == "InternalFrameTitlePane.closeButton") {
274 function = "close";
275 right_corner = true;
276 location = "right_right";
277 }
278
279 Node frame = getNode(frame_style_set, "frame", new String[] {
280 "focus", (active ? "yes" : "no"),
281 "state", (jif.isMaximum() ? "maximized" : "normal")
282 });
283
284 if (function != null && frame != null) {
285 Node frame_style = getNode("frame_style", new String[] {
286 "name", getStringAttr(frame, "style")
287 });
288 if (frame_style != null) {
289 Shape oldClip = g.getClip();
290 if ((right_corner && getBoolean("rounded_top_right", false)) ||
291 (left_corner && getBoolean("rounded_top_left", false))) {
292
293 Point buttonLoc = button.getLocation();
294 if (right_corner) {
295 g.setClip(getRoundedClipShape(0, 0, w, h,
296 12, 12, RoundRectClipShape.TOP_RIGHT));
297 } else {
298 g.setClip(getRoundedClipShape(0, 0, w, h,
299 11, 11, RoundRectClipShape.TOP_LEFT));
300 }
301
302 Rectangle clipBounds = oldClip.getBounds();
303 g.clipRect(clipBounds.x, clipBounds.y,
304 clipBounds.width, clipBounds.height);
305 }
306 drawButton(frame_style, location+"_background", state, g, w, h, jif);
307 drawButton(frame_style, function, state, g, w, h, jif);
308 g.setClip(oldClip);
309 }
310 }
311 }
312
313 protected void drawButton(Node frame_style, String function, String state,
314 Graphics g, int w, int h, JInternalFrame jif) {
315 Node buttonNode = getNode(frame_style, "button",
316 new String[] { "function", function, "state", state });
317 if (buttonNode == null && !state.equals("normal")) {
318 buttonNode = getNode(frame_style, "button",
319 new String[] { "function", function, "state", "normal" });
320 }
321 if (buttonNode != null) {
322 Node draw_ops;
323 String draw_ops_name = getStringAttr(buttonNode, "draw_ops");
324 if (draw_ops_name != null) {
325 draw_ops = getNode("draw_ops", new String[] { "name", draw_ops_name });
326 } else {
327 draw_ops = getNode(buttonNode, "draw_ops", null);
328 }
329 variables.put("width", w);
330 variables.put("height", h);
331 draw(draw_ops, g, jif);
332 }
333 }
334
335 void paintFrameBorder(SynthContext context, Graphics g, int x0, int y0, int width, int height) {
336 updateFrameGeometry(context);
337
338 this.context = context;
339 JComponent comp = context.getComponent();
340 JComponent titlePane = findChild(comp, "InternalFrame.northPane");
341
342 if (titlePane == null) {
343 return;
344 }
345
346 JInternalFrame jif = null;
347 if (comp instanceof JInternalFrame) {
348 jif = (JInternalFrame)comp;
349 } else if (comp instanceof JInternalFrame.JDesktopIcon) {
350 jif = ((JInternalFrame.JDesktopIcon)comp).getInternalFrame();
351 } else {
352 assert false : "component is not JInternalFrame or JInternalFrame.JDesktopIcon";
353 return;
354 }
355
356 boolean active = jif.isSelected();
357 Font oldFont = g.getFont();
358 g.setFont(titlePane.getFont());
359 g.translate(x0, y0);
360
361 Rectangle titleRect = calculateTitleArea(jif);
362 JComponent menuButton = findChild(titlePane, "InternalFrameTitlePane.menuButton");
363
364 Icon frameIcon = jif.getFrameIcon();
365 variables.put("mini_icon_width",
366 (frameIcon != null) ? frameIcon.getIconWidth() : 0);
367 variables.put("mini_icon_height",
368 (frameIcon != null) ? frameIcon.getIconHeight() : 0);
369 variables.put("title_width", calculateTitleTextWidth(g, jif));
370 FontMetrics fm = SwingUtilities2.getFontMetrics(jif, g);
371 variables.put("title_height", fm.getAscent() + fm.getDescent());
372
373
374 variables.put("icon_width", 32);
375 variables.put("icon_height", 32);
376
377 if (frame_style_set != null) {
378 Node frame = getNode(frame_style_set, "frame", new String[] {
379 "focus", (active ? "yes" : "no"),
380 "state", (jif.isMaximum() ? "maximized" : "normal")
381 });
382
383 if (frame != null) {
384 Node frame_style = getNode("frame_style", new String[] {
385 "name", getStringAttr(frame, "style")
386 });
387 if (frame_style != null) {
388 Shape oldClip = g.getClip();
389 boolean roundTopLeft = getBoolean("rounded_top_left", false);
390 boolean roundTopRight = getBoolean("rounded_top_right", false);
391 boolean roundBottomLeft = getBoolean("rounded_bottom_left", false);
392 boolean roundBottomRight = getBoolean("rounded_bottom_right", false);
393
394 if (roundTopLeft || roundTopRight || roundBottomLeft || roundBottomRight) {
395 jif.setOpaque(false);
396
397 g.setClip(getRoundedClipShape(0, 0, width, height, 12, 12,
398 (roundTopLeft ? RoundRectClipShape.TOP_LEFT : 0) |
399 (roundTopRight ? RoundRectClipShape.TOP_RIGHT : 0) |
400 (roundBottomLeft ? RoundRectClipShape.BOTTOM_LEFT : 0) |
401 (roundBottomRight ? RoundRectClipShape.BOTTOM_RIGHT : 0)));
402 }
403
404 Rectangle clipBounds = oldClip.getBounds();
405 g.clipRect(clipBounds.x, clipBounds.y,
406 clipBounds.width, clipBounds.height);
407
408 int titleHeight = titlePane.getHeight();
409
410 boolean minimized = jif.isIcon();
411 Insets insets = getBorderInsets(context, null);
412
413 int leftTitlebarEdge = getInt("left_titlebar_edge");
414 int rightTitlebarEdge = getInt("right_titlebar_edge");
415 int topTitlebarEdge = getInt("top_titlebar_edge");
416 int bottomTitlebarEdge = getInt("bottom_titlebar_edge");
417
418 if (!minimized) {
419 drawPiece(frame_style, g, "entire_background",
420 0, 0, width, height, jif);
421 }
422 drawPiece(frame_style, g, "titlebar",
423 0, 0, width, titleHeight, jif);
424 drawPiece(frame_style, g, "titlebar_middle",
425 leftTitlebarEdge, topTitlebarEdge,
426 width - leftTitlebarEdge - rightTitlebarEdge,
427 titleHeight - topTitlebarEdge - bottomTitlebarEdge,
428 jif);
429 drawPiece(frame_style, g, "left_titlebar_edge",
430 0, 0, leftTitlebarEdge, titleHeight, jif);
431 drawPiece(frame_style, g, "right_titlebar_edge",
432 width - rightTitlebarEdge, 0,
433 rightTitlebarEdge, titleHeight, jif);
434 drawPiece(frame_style, g, "top_titlebar_edge",
435 0, 0, width, topTitlebarEdge, jif);
436 drawPiece(frame_style, g, "bottom_titlebar_edge",
437 0, titleHeight - bottomTitlebarEdge,
438 width, bottomTitlebarEdge, jif);
439 drawPiece(frame_style, g, "title",
440 titleRect.x, titleRect.y, titleRect.width, titleRect.height, jif);
441 if (!minimized) {
442 drawPiece(frame_style, g, "left_edge",
443 0, titleHeight, insets.left, height-titleHeight, jif);
444 drawPiece(frame_style, g, "right_edge",
445 width-insets.right, titleHeight, insets.right, height-titleHeight, jif);
446 drawPiece(frame_style, g, "bottom_edge",
447 0, height - insets.bottom, width, insets.bottom, jif);
448 drawPiece(frame_style, g, "overlay",
449 0, 0, width, height, jif);
450 }
451 g.setClip(oldClip);
452 }
453 }
454 }
455 g.translate(-x0, -y0);
456 g.setFont(oldFont);
457 }
458
459
460
461 private static class Privileged implements PrivilegedAction<Object> {
462 private static int GET_THEME_DIR = 0;
463 private static int GET_USER_THEME = 1;
464 private static int GET_IMAGE = 2;
465 private int type;
466 private Object arg;
467
468 public Object doPrivileged(int type, Object arg) {
469 this.type = type;
470 this.arg = arg;
471 return AccessController.doPrivileged(this);
472 }
473
474 public Object run() {
475 if (type == GET_THEME_DIR) {
476 String sep = File.separator;
477 String[] dirs = new String[] {
478 userHome + sep + ".themes",
479 System.getProperty("swing.metacitythemedir"),
480 "/usr/share/themes",
481 "/usr/gnome/share/themes",
482 "/opt/gnome2/share/themes"
483 };
484
485 URL themeDir = null;
486 for (int i = 0; i < dirs.length; i++) {
487
488 if (dirs[i] == null) {
489 continue;
490 }
491 File dir =
492 new File(dirs[i] + sep + arg + sep + "metacity-1");
493 if (new File(dir, "metacity-theme-1.xml").canRead()) {
494 try {
495 themeDir = dir.toURI().toURL();
496 } catch (MalformedURLException ex) {
497 themeDir = null;
498 }
499 break;
500 }
501 }
502 if (themeDir == null) {
503 String filename = "resources/metacity/" + arg +
504 "/metacity-1/metacity-theme-1.xml";
505 URL url = getClass().getResource(filename);
506 if (url != null) {
507 String str = url.toString();
508 try {
509 themeDir = new URL(str.substring(0, str.lastIndexOf('/'))+"/");
510 } catch (MalformedURLException ex) {
511 themeDir = null;
512 }
513 }
514 }
515 return themeDir;
516 } else if (type == GET_USER_THEME) {
517 try {
518
519 userHome = System.getProperty("user.home");
520
521 String theme = System.getProperty("swing.metacitythemename");
522 if (theme != null) {
523 return theme;
524 }
525
526
527 URL url = new URL(new File(userHome).toURI().toURL(),
528 ".gconf/apps/metacity/general/%25gconf.xml");
529
530 Reader reader = new InputStreamReader(url.openStream(), "ISO-8859-1");
531 char[] buf = new char[1024];
532 StringBuffer strBuf = new StringBuffer();
533 int n;
534 while ((n = reader.read(buf)) >= 0) {
535 strBuf.append(buf, 0, n);
536 }
537 reader.close();
538 String str = strBuf.toString();
539 if (str != null) {
540 String strLowerCase = str.toLowerCase();
541 int i = strLowerCase.indexOf("<entry name=\"theme\"");
542 if (i >= 0) {
543 i = strLowerCase.indexOf("<stringvalue>", i);
544 if (i > 0) {
545 i += "<stringvalue>".length();
546 int i2 = str.indexOf("<", i);
547 return str.substring(i, i2);
548 }
549 }
550 }
551 } catch (MalformedURLException ex) {
552
553 } catch (IOException ex) {
554
555 }
556 return null;
557 } else if (type == GET_IMAGE) {
558 return new ImageIcon((URL)arg).getImage();
559 } else {
560 return null;
561 }
562 }
563 }
564
565 private static URL getThemeDir(String themeName) {
566 return (URL)new Privileged().doPrivileged(Privileged.GET_THEME_DIR, themeName);
567 }
568
569 private static String getUserTheme() {
570 return (String)new Privileged().doPrivileged(Privileged.GET_USER_THEME, null);
571 }
572
573 protected void tileImage(Graphics g, Image image, int x0, int y0, int w, int h, float[] alphas) {
574 Graphics2D g2 = (Graphics2D)g;
575 Composite oldComp = g2.getComposite();
576
577 int sw = image.getWidth(null);
578 int sh = image.getHeight(null);
579 int y = y0;
580 while (y < y0 + h) {
581 sh = Math.min(sh, y0 + h - y);
582 int x = x0;
583 while (x < x0 + w) {
584 float f = (alphas.length - 1.0F) * x / (x0 + w);
585 int i = (int)f;
586 f -= (int)f;
587 float alpha = (1-f) * alphas[i];
588 if (i+1 < alphas.length) {
589 alpha += f * alphas[i+1];
590 }
591 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
592 int swm = Math.min(sw, x0 + w - x);
593 g.drawImage(image, x, y, x+swm, y+sh, 0, 0, swm, sh, null);
594 x += swm;
595 }
596 y += sh;
597 }
598 g2.setComposite(oldComp);
599 }
600
601 private HashMap<String, Image> images = new HashMap<String, Image>();
602
603 protected Image getImage(String key, Color c) {
604 Image image = images.get(key+"-"+c.getRGB());
605 if (image == null) {
606 image = imageFilter.colorize(getImage(key), c);
607 if (image != null) {
608 images.put(key+"-"+c.getRGB(), image);
609 }
610 }
611 return image;
612 }
613
614 protected Image getImage(String key) {
615 Image image = images.get(key);
616 if (image == null) {
617 if (themeDir != null) {
618 try {
619 URL url = new URL(themeDir, key);
620 image = (Image)new Privileged().doPrivileged(Privileged.GET_IMAGE, url);
621 } catch (MalformedURLException ex) {
622
623 }
624 }
625 if (image != null) {
626 images.put(key, image);
627 }
628 }
629 return image;
630 }
631
632 private class ColorizeImageFilter extends RGBImageFilter {
633 double cr, cg, cb;
634
635 public ColorizeImageFilter() {
636 canFilterIndexColorModel = true;
637 }
638
639 public void setColor(Color color) {
640 cr = color.getRed() / 255.0;
641 cg = color.getGreen() / 255.0;
642 cb = color.getBlue() / 255.0;
643 }
644
645 public Image colorize(Image fromImage, Color c) {
646 setColor(c);
647 ImageProducer producer = new FilteredImageSource(fromImage.getSource(), this);
648 return new ImageIcon(context.getComponent().createImage(producer)).getImage();
649 }
650
651 public int filterRGB(int x, int y, int rgb) {
652
653 double grayLevel = 2 * (rgb & 0xff) / 255.0;
654 double r, g, b;
655
656 if (grayLevel <= 1.0) {
657 r = cr * grayLevel;
658 g = cg * grayLevel;
659 b = cb * grayLevel;
660 } else {
661 grayLevel -= 1.0;
662 r = cr + (1.0 - cr) * grayLevel;
663 g = cg + (1.0 - cg) * grayLevel;
664 b = cb + (1.0 - cb) * grayLevel;
665 }
666
667 return ((rgb & 0xff000000) +
668 (((int)(r * 255)) << 16) +
669 (((int)(g * 255)) << 8) +
670 (int)(b * 255));
671 }
672 }
673
674 protected static JComponent findChild(JComponent parent, String name) {
675 int n = parent.getComponentCount();
676 for (int i = 0; i < n; i++) {
677 JComponent c = (JComponent)parent.getComponent(i);
678 if (name.equals(c.getName())) {
679 return c;
680 }
681 }
682 return null;
683 }
684
685
686 protected class TitlePaneLayout implements LayoutManager {
687 public void addLayoutComponent(String name, Component c) {}
688 public void removeLayoutComponent(Component c) {}
689 public Dimension preferredLayoutSize(Container c) {
690 return minimumLayoutSize(c);
691 }
692
693 public Dimension minimumLayoutSize(Container c) {
694 JComponent titlePane = (JComponent)c;
695 Container titlePaneParent = titlePane.getParent();
696 JInternalFrame frame;
697 if (titlePaneParent instanceof JInternalFrame) {
698 frame = (JInternalFrame)titlePaneParent;
699 } else if (titlePaneParent instanceof JInternalFrame.JDesktopIcon) {
700 frame = ((JInternalFrame.JDesktopIcon)titlePaneParent).getInternalFrame();
701 } else {
702 return null;
703 }
704
705 Dimension buttonDim = calculateButtonSize(titlePane);
706 Insets title_border = (Insets)getFrameGeometry().get("title_border");
707 Insets button_border = (Insets)getFrameGeometry().get("button_border");
708
709
710 int width = getInt("left_titlebar_edge") + buttonDim.width + getInt("right_titlebar_edge");
711 if (title_border != null) {
712 width += title_border.left + title_border.right;
713 }
714 if (frame.isClosable()) {
715 width += buttonDim.width;
716 }
717 if (frame.isMaximizable()) {
718 width += buttonDim.width;
719 }
720 if (frame.isIconifiable()) {
721 width += buttonDim.width;
722 }
723 FontMetrics fm = frame.getFontMetrics(titlePane.getFont());
724 String frameTitle = frame.getTitle();
725 int title_w = frameTitle != null ? SwingUtilities2.stringWidth(
726 frame, fm, frameTitle) : 0;
727 int title_length = frameTitle != null ? frameTitle.length() : 0;
728
729
730 if (title_length > 3) {
731 int subtitle_w = SwingUtilities2.stringWidth(
732 frame, fm, frameTitle.substring(0, 3) + "...");
733 width += (title_w < subtitle_w) ? title_w : subtitle_w;
734 } else {
735 width += title_w;
736 }
737
738
739 int titleHeight = fm.getHeight() + getInt("title_vertical_pad");
740 if (title_border != null) {
741 titleHeight += title_border.top + title_border.bottom;
742 }
743 int buttonHeight = buttonDim.height;
744 if (button_border != null) {
745 buttonHeight += button_border.top + button_border.bottom;
746 }
747 int height = Math.max(buttonHeight, titleHeight);
748
749 return new Dimension(width, height);
750 }
751
752 public void layoutContainer(Container c) {
753 JComponent titlePane = (JComponent)c;
754 Container titlePaneParent = titlePane.getParent();
755 JInternalFrame frame;
756 if (titlePaneParent instanceof JInternalFrame) {
757 frame = (JInternalFrame)titlePaneParent;
758 } else if (titlePaneParent instanceof JInternalFrame.JDesktopIcon) {
759 frame = ((JInternalFrame.JDesktopIcon)titlePaneParent).getInternalFrame();
760 } else {
761 return;
762 }
763 Map gm = getFrameGeometry();
764
765 int w = titlePane.getWidth();
766 int h = titlePane.getHeight();
767
768 JComponent menuButton = findChild(titlePane, "InternalFrameTitlePane.menuButton");
769 JComponent minimizeButton = findChild(titlePane, "InternalFrameTitlePane.iconifyButton");
770 JComponent maximizeButton = findChild(titlePane, "InternalFrameTitlePane.maximizeButton");
771 JComponent closeButton = findChild(titlePane, "InternalFrameTitlePane.closeButton");
772
773 Insets button_border = (Insets)gm.get("button_border");
774 Dimension buttonDim = calculateButtonSize(titlePane);
775
776 int y = (button_border != null) ? button_border.top : 0;
777 if (titlePaneParent.getComponentOrientation().isLeftToRight()) {
778 int x = getInt("left_titlebar_edge");
779
780 menuButton.setBounds(x, y, buttonDim.width, buttonDim.height);
781
782 x = w - buttonDim.width - getInt("right_titlebar_edge");
783 if (button_border != null) {
784 x -= button_border.right;
785 }
786
787 if (frame.isClosable()) {
788 closeButton.setBounds(x, y, buttonDim.width, buttonDim.height);
789 x -= buttonDim.width;
790 }
791
792 if (frame.isMaximizable()) {
793 maximizeButton.setBounds(x, y, buttonDim.width, buttonDim.height);
794 x -= buttonDim.width;
795 }
796
797 if (frame.isIconifiable()) {
798 minimizeButton.setBounds(x, y, buttonDim.width, buttonDim.height);
799 }
800 } else {
801 int x = w - buttonDim.width - getInt("right_titlebar_edge");
802
803 menuButton.setBounds(x, y, buttonDim.width, buttonDim.height);
804
805 x = getInt("left_titlebar_edge");
806 if (button_border != null) {
807 x += button_border.left;
808 }
809
810 if (frame.isClosable()) {
811 closeButton.setBounds(x, y, buttonDim.width, buttonDim.height);
812 x += buttonDim.width;
813 }
814
815 if (frame.isMaximizable()) {
816 maximizeButton.setBounds(x, y, buttonDim.width, buttonDim.height);
817 x += buttonDim.width;
818 }
819
820 if (frame.isIconifiable()) {
821 minimizeButton.setBounds(x, y, buttonDim.width, buttonDim.height);
822 }
823 }
824 }
825 }
826
827 protected Map getFrameGeometry() {
828 return frameGeometry;
829 }
830
831 protected void setFrameGeometry(JComponent titlePane, Map gm) {
832 this.frameGeometry = gm;
833 if (getInt("top_height") == 0 && titlePane != null) {
834 gm.put("top_height", Integer.valueOf(titlePane.getHeight()));
835 }
836 }
837
838 protected int getInt(String key) {
839 Integer i = (Integer)frameGeometry.get(key);
840 if (i == null) {
841 i = variables.get(key);
842 }
843 return (i != null) ? i.intValue() : 0;
844 }
845
846 protected boolean getBoolean(String key, boolean fallback) {
847 Boolean b = (Boolean)frameGeometry.get(key);
848 return (b != null) ? b.booleanValue() : fallback;
849 }
850
851
852 protected void drawArc(Node node, Graphics g) {
853 NamedNodeMap attrs = node.getAttributes();
854 Color color = parseColor(getStringAttr(attrs, "color"));
855 int x = aee.evaluate(getStringAttr(attrs, "x"));
856 int y = aee.evaluate(getStringAttr(attrs, "y"));
857 int w = aee.evaluate(getStringAttr(attrs, "width"));
858 int h = aee.evaluate(getStringAttr(attrs, "height"));
859 int start_angle = aee.evaluate(getStringAttr(attrs, "start_angle"));
860 int extent_angle = aee.evaluate(getStringAttr(attrs, "extent_angle"));
861 boolean filled = getBooleanAttr(node, "filled", false);
862 if (getInt("width") == -1) {
863 x -= w;
864 }
865 if (getInt("height") == -1) {
866 y -= h;
867 }
868 g.setColor(color);
869 if (filled) {
870 g.fillArc(x, y, w, h, start_angle, extent_angle);
871 } else {
872 g.drawArc(x, y, w, h, start_angle, extent_angle);
873 }
874 }
875
876 protected void drawLine(Node node, Graphics g) {
877 NamedNodeMap attrs = node.getAttributes();
878 Color color = parseColor(getStringAttr(attrs, "color"));
879 int x1 = aee.evaluate(getStringAttr(attrs, "x1"));
880 int y1 = aee.evaluate(getStringAttr(attrs, "y1"));
881 int x2 = aee.evaluate(getStringAttr(attrs, "x2"));
882 int y2 = aee.evaluate(getStringAttr(attrs, "y2"));
883 int lineWidth = aee.evaluate(getStringAttr(attrs, "width"), 1);
884 g.setColor(color);
885 if (lineWidth != 1) {
886 Graphics2D g2d = (Graphics2D)g;
887 Stroke stroke = g2d.getStroke();
888 g2d.setStroke(new BasicStroke((float)lineWidth));
889 g2d.drawLine(x1, y1, x2, y2);
890 g2d.setStroke(stroke);
891 } else {
892 g.drawLine(x1, y1, x2, y2);
893 }
894 }
895
896 protected void drawRectangle(Node node, Graphics g) {
897 NamedNodeMap attrs = node.getAttributes();
898 Color color = parseColor(getStringAttr(attrs, "color"));
899 boolean filled = getBooleanAttr(node, "filled", false);
900 int x = aee.evaluate(getStringAttr(attrs, "x"));
901 int y = aee.evaluate(getStringAttr(attrs, "y"));
902 int w = aee.evaluate(getStringAttr(attrs, "width"));
903 int h = aee.evaluate(getStringAttr(attrs, "height"));
904 g.setColor(color);
905 if (getInt("width") == -1) {
906 x -= w;
907 }
908 if (getInt("height") == -1) {
909 y -= h;
910 }
911 if (filled) {
912 g.fillRect(x, y, w, h);
913 } else {
914 g.drawRect(x, y, w, h);
915 }
916 }
917
918 protected void drawTile(Node node, Graphics g, JInternalFrame jif) {
919 NamedNodeMap attrs = node.getAttributes();
920 int x0 = aee.evaluate(getStringAttr(attrs, "x"));
921 int y0 = aee.evaluate(getStringAttr(attrs, "y"));
922 int w = aee.evaluate(getStringAttr(attrs, "width"));
923 int h = aee.evaluate(getStringAttr(attrs, "height"));
924 int tw = aee.evaluate(getStringAttr(attrs, "tile_width"));
925 int th = aee.evaluate(getStringAttr(attrs, "tile_height"));
926 int width = getInt("width");
927 int height = getInt("height");
928 if (width == -1) {
929 x0 -= w;
930 }
931 if (height == -1) {
932 y0 -= h;
933 }
934 Shape oldClip = g.getClip();
935 if (g instanceof Graphics2D) {
936 ((Graphics2D)g).clip(new Rectangle(x0, y0, w, h));
937 }
938 variables.put("width", tw);
939 variables.put("height", th);
940
941 Node draw_ops = getNode("draw_ops", new String[] { "name", getStringAttr(node, "name") });
942
943 int y = y0;
944 while (y < y0 + h) {
945 int x = x0;
946 while (x < x0 + w) {
947 g.translate(x, y);
948 draw(draw_ops, g, jif);
949 g.translate(-x, -y);
950 x += tw;
951 }
952 y += th;
953 }
954
955 variables.put("width", width);
956 variables.put("height", height);
957 g.setClip(oldClip);
958 }
959
960 protected void drawTint(Node node, Graphics g) {
961 NamedNodeMap attrs = node.getAttributes();
962 Color color = parseColor(getStringAttr(attrs, "color"));
963 float alpha = Float.parseFloat(getStringAttr(attrs, "alpha"));
964 int x = aee.evaluate(getStringAttr(attrs, "x"));
965 int y = aee.evaluate(getStringAttr(attrs, "y"));
966 int w = aee.evaluate(getStringAttr(attrs, "width"));
967 int h = aee.evaluate(getStringAttr(attrs, "height"));
968 if (getInt("width") == -1) {
969 x -= w;
970 }
971 if (getInt("height") == -1) {
972 y -= h;
973 }
974 if (g instanceof Graphics2D) {
975 Graphics2D g2 = (Graphics2D)g;
976 Composite oldComp = g2.getComposite();
977 AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);
978 g2.setComposite(ac);
979 g2.setColor(color);
980 g2.fillRect(x, y, w, h);
981 g2.setComposite(oldComp);
982 }
983 }
984
985 protected void drawTitle(Node node, Graphics g, JInternalFrame jif) {
986 NamedNodeMap attrs = node.getAttributes();
987 String colorStr = getStringAttr(attrs, "color");
988 int i = colorStr.indexOf("gtk:fg[");
989 if (i > 0) {
990 colorStr = colorStr.substring(0, i) + "gtk:text[" + colorStr.substring(i+7);
991 }
992 Color color = parseColor(colorStr);
993 int x = aee.evaluate(getStringAttr(attrs, "x"));
994 int y = aee.evaluate(getStringAttr(attrs, "y"));
995
996 String title = jif.getTitle();
997 if (title != null) {
998 FontMetrics fm = SwingUtilities2.getFontMetrics(jif, g);
999 title = SwingUtilities2.clipStringIfNecessary(jif, fm, title,
1000 calculateTitleArea(jif).width);
1001 g.setColor(color);
1002 SwingUtilities2.drawString(jif, g, title, x, y + fm.getAscent());
1003 }
1004 }
1005
1006 protected Dimension calculateButtonSize(JComponent titlePane) {
1007 int buttonHeight = getInt("button_height");
1008 if (buttonHeight == 0) {
1009 buttonHeight = titlePane.getHeight();
1010 if (buttonHeight == 0) {
1011 buttonHeight = 13;
1012 } else {
1013 Insets button_border = (Insets)frameGeometry.get("button_border");
1014 if (button_border != null) {
1015 buttonHeight -= (button_border.top + button_border.bottom);
1016 }
1017 }
1018 }
1019 int buttonWidth = getInt("button_width");
1020 if (buttonWidth == 0) {
1021 buttonWidth = buttonHeight;
1022 Float aspect_ratio = (Float)frameGeometry.get("aspect_ratio");
1023 if (aspect_ratio != null) {
1024 buttonWidth = (int)(buttonHeight / aspect_ratio.floatValue());
1025 }
1026 }
1027 return new Dimension(buttonWidth, buttonHeight);
1028 }
1029
1030 protected Rectangle calculateTitleArea(JInternalFrame jif) {
1031 JComponent titlePane = findChild(jif, "InternalFrame.northPane");
1032 Dimension buttonDim = calculateButtonSize(titlePane);
1033 Insets title_border = (Insets)frameGeometry.get("title_border");
1034 Insets button_border = (Insets)getFrameGeometry().get("button_border");
1035
1036 Rectangle r = new Rectangle();
1037 r.x = getInt("left_titlebar_edge");
1038 r.y = 0;
1039 r.height = titlePane.getHeight();
1040 if (title_border != null) {
1041 r.x += title_border.left;
1042 r.y += title_border.top;
1043 r.height -= (title_border.top + title_border.bottom);
1044 }
1045
1046 if (titlePane.getParent().getComponentOrientation().isLeftToRight()) {
1047 r.x += buttonDim.width;
1048 if (button_border != null) {
1049 r.x += button_border.left;
1050 }
1051 r.width = titlePane.getWidth() - r.x - getInt("right_titlebar_edge");
1052 if (jif.isClosable()) {
1053 r.width -= buttonDim.width;
1054 }
1055 if (jif.isMaximizable()) {
1056 r.width -= buttonDim.width;
1057 }
1058 if (jif.isIconifiable()) {
1059 r.width -= buttonDim.width;
1060 }
1061 } else {
1062 if (jif.isClosable()) {
1063 r.x += buttonDim.width;
1064 }
1065 if (jif.isMaximizable()) {
1066 r.x += buttonDim.width;
1067 }
1068 if (jif.isIconifiable()) {
1069 r.x += buttonDim.width;
1070 }
1071 r.width = titlePane.getWidth() - r.x - getInt("right_titlebar_edge")
1072 - buttonDim.width;
1073 if (button_border != null) {
1074 r.x -= button_border.right;
1075 }
1076 }
1077 if (title_border != null) {
1078 r.width -= title_border.right;
1079 }
1080 return r;
1081 }
1082
1083
1084 protected int calculateTitleTextWidth(Graphics g, JInternalFrame jif) {
1085 String title = jif.getTitle();
1086 if (title != null) {
1087 Rectangle r = calculateTitleArea(jif);
1088 return Math.min(SwingUtilities2.stringWidth(jif,
1089 SwingUtilities2.getFontMetrics(jif, g), title), r.width);
1090 }
1091 return 0;
1092 }
1093
1094 protected void setClip(Node node, Graphics g) {
1095 NamedNodeMap attrs = node.getAttributes();
1096 int x = aee.evaluate(getStringAttr(attrs, "x"));
1097 int y = aee.evaluate(getStringAttr(attrs, "y"));
1098 int w = aee.evaluate(getStringAttr(attrs, "width"));
1099 int h = aee.evaluate(getStringAttr(attrs, "height"));
1100 if (getInt("width") == -1) {
1101 x -= w;
1102 }
1103 if (getInt("height") == -1) {
1104 y -= h;
1105 }
1106 if (g instanceof Graphics2D) {
1107 ((Graphics2D)g).clip(new Rectangle(x, y, w, h));
1108 }
1109 }
1110
1111 protected void drawGTKArrow(Node node, Graphics g) {
1112 NamedNodeMap attrs = node.getAttributes();
1113 String arrow = getStringAttr(attrs, "arrow");
1114 String shadow = getStringAttr(attrs, "shadow");
1115 String stateStr = getStringAttr(attrs, "state").toUpperCase();
1116 int x = aee.evaluate(getStringAttr(attrs, "x"));
1117 int y = aee.evaluate(getStringAttr(attrs, "y"));
1118 int w = aee.evaluate(getStringAttr(attrs, "width"));
1119 int h = aee.evaluate(getStringAttr(attrs, "height"));
1120
1121 int state = -1;
1122 if ("NORMAL".equals(stateStr)) {
1123 state = ENABLED;
1124 } else if ("SELECTED".equals(stateStr)) {
1125 state = SELECTED;
1126 } else if ("INSENSITIVE".equals(stateStr)) {
1127 state = DISABLED;
1128 } else if ("PRELIGHT".equals(stateStr)) {
1129 state = MOUSE_OVER;
1130 }
1131
1132 ShadowType shadowType = null;
1133 if ("in".equals(shadow)) {
1134 shadowType = ShadowType.IN;
1135 } else if ("out".equals(shadow)) {
1136 shadowType = ShadowType.OUT;
1137 } else if ("etched_in".equals(shadow)) {
1138 shadowType = ShadowType.ETCHED_IN;
1139 } else if ("etched_out".equals(shadow)) {
1140 shadowType = ShadowType.ETCHED_OUT;
1141 } else if ("none".equals(shadow)) {
1142 shadowType = ShadowType.NONE;
1143 }
1144
1145 ArrowType direction = null;
1146 if ("up".equals(arrow)) {
1147 direction = ArrowType.UP;
1148 } else if ("down".equals(arrow)) {
1149 direction = ArrowType.DOWN;
1150 } else if ("left".equals(arrow)) {
1151 direction = ArrowType.LEFT;
1152 } else if ("right".equals(arrow)) {
1153 direction = ArrowType.RIGHT;
1154 }
1155
1156 GTKPainter.INSTANCE.paintMetacityElement(context, g, state,
1157 "metacity-arrow", x, y, w, h, shadowType, direction);
1158 }
1159
1160 protected void drawGTKBox(Node node, Graphics g) {
1161 NamedNodeMap attrs = node.getAttributes();
1162 String shadow = getStringAttr(attrs, "shadow");
1163 String stateStr = getStringAttr(attrs, "state").toUpperCase();
1164 int x = aee.evaluate(getStringAttr(attrs, "x"));
1165 int y = aee.evaluate(getStringAttr(attrs, "y"));
1166 int w = aee.evaluate(getStringAttr(attrs, "width"));
1167 int h = aee.evaluate(getStringAttr(attrs, "height"));
1168
1169 int state = -1;
1170 if ("NORMAL".equals(stateStr)) {
1171 state = ENABLED;
1172 } else if ("SELECTED".equals(stateStr)) {
1173 state = SELECTED;
1174 } else if ("INSENSITIVE".equals(stateStr)) {
1175 state = DISABLED;
1176 } else if ("PRELIGHT".equals(stateStr)) {
1177 state = MOUSE_OVER;
1178 }
1179
1180 ShadowType shadowType = null;
1181 if ("in".equals(shadow)) {
1182 shadowType = ShadowType.IN;
1183 } else if ("out".equals(shadow)) {
1184 shadowType = ShadowType.OUT;
1185 } else if ("etched_in".equals(shadow)) {
1186 shadowType = ShadowType.ETCHED_IN;
1187 } else if ("etched_out".equals(shadow)) {
1188 shadowType = ShadowType.ETCHED_OUT;
1189 } else if ("none".equals(shadow)) {
1190 shadowType = ShadowType.NONE;
1191 }
1192 GTKPainter.INSTANCE.paintMetacityElement(context, g, state,
1193 "metacity-box", x, y, w, h, shadowType, null);
1194 }
1195
1196 protected void drawGTKVLine(Node node, Graphics g) {
1197 NamedNodeMap attrs = node.getAttributes();
1198 String stateStr = getStringAttr(attrs, "state").toUpperCase();
1199
1200 int x = aee.evaluate(getStringAttr(attrs, "x"));
1201 int y1 = aee.evaluate(getStringAttr(attrs, "y1"));
1202 int y2 = aee.evaluate(getStringAttr(attrs, "y2"));
1203
1204 int state = -1;
1205 if ("NORMAL".equals(stateStr)) {
1206 state = ENABLED;
1207 } else if ("SELECTED".equals(stateStr)) {
1208 state = SELECTED;
1209 } else if ("INSENSITIVE".equals(stateStr)) {
1210 state = DISABLED;
1211 } else if ("PRELIGHT".equals(stateStr)) {
1212 state = MOUSE_OVER;
1213 }
1214
1215 GTKPainter.INSTANCE.paintMetacityElement(context, g, state,
1216 "metacity-vline", x, y1, 1, y2 - y1, null, null);
1217 }
1218
1219 protected void drawGradient(Node node, Graphics g) {
1220 NamedNodeMap attrs = node.getAttributes();
1221 String type = getStringAttr(attrs, "type");
1222 float alpha = getFloatAttr(node, "alpha", -1F);
1223 int x = aee.evaluate(getStringAttr(attrs, "x"));
1224 int y = aee.evaluate(getStringAttr(attrs, "y"));
1225 int w = aee.evaluate(getStringAttr(attrs, "width"));
1226 int h = aee.evaluate(getStringAttr(attrs, "height"));
1227 if (getInt("width") == -1) {
1228 x -= w;
1229 }
1230 if (getInt("height") == -1) {
1231 y -= h;
1232 }
1233
1234
1235 Node[] colorNodes = getNodesByName(node, "color");
1236 Color[] colors = new Color[colorNodes.length];
1237 for (int i = 0; i < colorNodes.length; i++) {
1238 colors[i] = parseColor(getStringAttr(colorNodes[i], "value"));
1239 }
1240
1241 boolean horizontal = ("diagonal".equals(type) || "horizontal".equals(type));
1242 boolean vertical = ("diagonal".equals(type) || "vertical".equals(type));
1243
1244 if (g instanceof Graphics2D) {
1245 Graphics2D g2 = (Graphics2D)g;
1246 Composite oldComp = g2.getComposite();
1247 if (alpha >= 0F) {
1248 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
1249 }
1250 int n = colors.length - 1;
1251 for (int i = 0; i < n; i++) {
1252 g2.setPaint(new GradientPaint(x + (horizontal ? (i*w/n) : 0),
1253 y + (vertical ? (i*h/n) : 0),
1254 colors[i],
1255 x + (horizontal ? ((i+1)*w/n) : 0),
1256 y + (vertical ? ((i+1)*h/n) : 0),
1257 colors[i+1]));
1258 g2.fillRect(x + (horizontal ? (i*w/n) : 0),
1259 y + (vertical ? (i*h/n) : 0),
1260 (horizontal ? (w/n) : w),
1261 (vertical ? (h/n) : h));
1262 }
1263 g2.setComposite(oldComp);
1264 }
1265 }
1266
1267 protected void drawImage(Node node, Graphics g) {
1268 NamedNodeMap attrs = node.getAttributes();
1269 String filename = getStringAttr(attrs, "filename");
1270 String colorizeStr = getStringAttr(attrs, "colorize");
1271 Color colorize = (colorizeStr != null) ? parseColor(colorizeStr) : null;
1272 String alpha = getStringAttr(attrs, "alpha");
1273 Image object = (colorize != null) ? getImage(filename, colorize) : getImage(filename);
1274 variables.put("object_width", object.getWidth(null));
1275 variables.put("object_height", object.getHeight(null));
1276 String fill_type = getStringAttr(attrs, "fill_type");
1277 int x = aee.evaluate(getStringAttr(attrs, "x"));
1278 int y = aee.evaluate(getStringAttr(attrs, "y"));
1279 int w = aee.evaluate(getStringAttr(attrs, "width"));
1280 int h = aee.evaluate(getStringAttr(attrs, "height"));
1281 if (getInt("width") == -1) {
1282 x -= w;
1283 }
1284 if (getInt("height") == -1) {
1285 y -= h;
1286 }
1287
1288 if (alpha != null) {
1289 if ("tile".equals(fill_type)) {
1290 StringTokenizer tokenizer = new StringTokenizer(alpha, ":");
1291 float[] alphas = new float[tokenizer.countTokens()];
1292 for (int i = 0; i < alphas.length; i++) {
1293 alphas[i] = Float.parseFloat(tokenizer.nextToken());
1294 }
1295 tileImage(g, object, x, y, w, h, alphas);
1296 } else {
1297 float a = Float.parseFloat(alpha);
1298 if (g instanceof Graphics2D) {
1299 Graphics2D g2 = (Graphics2D)g;
1300 Composite oldComp = g2.getComposite();
1301 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, a));
1302 g2.drawImage(object, x, y, w, h, null);
1303 g2.setComposite(oldComp);
1304 }
1305 }
1306 } else {
1307 g.drawImage(object, x, y, w, h, null);
1308 }
1309 }
1310
1311 protected void drawIcon(Node node, Graphics g, JInternalFrame jif) {
1312 Icon icon = jif.getFrameIcon();
1313 if (icon == null) {
1314 return;
1315 }
1316
1317 NamedNodeMap attrs = node.getAttributes();
1318 String alpha = getStringAttr(attrs, "alpha");
1319 int x = aee.evaluate(getStringAttr(attrs, "x"));
1320 int y = aee.evaluate(getStringAttr(attrs, "y"));
1321 int w = aee.evaluate(getStringAttr(attrs, "width"));
1322 int h = aee.evaluate(getStringAttr(attrs, "height"));
1323 if (getInt("width") == -1) {
1324 x -= w;
1325 }
1326 if (getInt("height") == -1) {
1327 y -= h;
1328 }
1329
1330 if (alpha != null) {
1331 float a = Float.parseFloat(alpha);
1332 if (g instanceof Graphics2D) {
1333 Graphics2D g2 = (Graphics2D)g;
1334 Composite oldComp = g2.getComposite();
1335 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, a));
1336 icon.paintIcon(jif, g, x, y);
1337 g2.setComposite(oldComp);
1338 }
1339 } else {
1340 icon.paintIcon(jif, g, x, y);
1341 }
1342 }
1343
1344 protected void drawInclude(Node node, Graphics g, JInternalFrame jif) {
1345 int oldWidth = getInt("width");
1346 int oldHeight = getInt("height");
1347
1348 NamedNodeMap attrs = node.getAttributes();
1349 int x = aee.evaluate(getStringAttr(attrs, "x"), 0);
1350 int y = aee.evaluate(getStringAttr(attrs, "y"), 0);
1351 int w = aee.evaluate(getStringAttr(attrs, "width"), -1);
1352 int h = aee.evaluate(getStringAttr(attrs, "height"), -1);
1353
1354 if (w != -1) {
1355 variables.put("width", w);
1356 }
1357 if (h != -1) {
1358 variables.put("height", h);
1359 }
1360
1361 Node draw_ops = getNode("draw_ops", new String[] {
1362 "name", getStringAttr(node, "name")
1363 });
1364 g.translate(x, y);
1365 draw(draw_ops, g, jif);
1366 g.translate(-x, -y);
1367
1368 if (w != -1) {
1369 variables.put("width", oldWidth);
1370 }
1371 if (h != -1) {
1372 variables.put("height", oldHeight);
1373 }
1374 }
1375
1376 protected void draw(Node draw_ops, Graphics g, JInternalFrame jif) {
1377 if (draw_ops != null) {
1378 NodeList nodes = draw_ops.getChildNodes();
1379 if (nodes != null) {
1380 Shape oldClip = g.getClip();
1381 for (int i = 0; i < nodes.getLength(); i++) {
1382 Node child = nodes.item(i);
1383 if (child.getNodeType() == Node.ELEMENT_NODE) {
1384 try {
1385 String name = child.getNodeName();
1386 if ("include".equals(name)) {
1387 drawInclude(child, g, jif);
1388 } else if ("arc".equals(name)) {
1389 drawArc(child, g);
1390 } else if ("clip".equals(name)) {
1391 setClip(child, g);
1392 } else if ("gradient".equals(name)) {
1393 drawGradient(child, g);
1394 } else if ("gtk_arrow".equals(name)) {
1395 drawGTKArrow(child, g);
1396 } else if ("gtk_box".equals(name)) {
1397 drawGTKBox(child, g);
1398 } else if ("gtk_vline".equals(name)) {
1399 drawGTKVLine(child, g);
1400 } else if ("image".equals(name)) {
1401 drawImage(child, g);
1402 } else if ("icon".equals(name)) {
1403 drawIcon(child, g, jif);
1404 } else if ("line".equals(name)) {
1405 drawLine(child, g);
1406 } else if ("rectangle".equals(name)) {
1407 drawRectangle(child, g);
1408 } else if ("tint".equals(name)) {
1409 drawTint(child, g);
1410 } else if ("tile".equals(name)) {
1411 drawTile(child, g, jif);
1412 } else if ("title".equals(name)) {
1413 drawTitle(child, g, jif);
1414 } else {
1415 System.err.println("Unknown Metacity drawing op: "+child);
1416 }
1417 } catch (NumberFormatException ex) {
1418 logError(themeName, ex);
1419 }
1420 }
1421 }
1422 g.setClip(oldClip);
1423 }
1424 }
1425 }
1426
1427 protected void drawPiece(Node frame_style, Graphics g, String position, int x, int y,
1428 int width, int height, JInternalFrame jif) {
1429 Node piece = getNode(frame_style, "piece", new String[] { "position", position });
1430 if (piece != null) {
1431 Node draw_ops;
1432 String draw_ops_name = getStringAttr(piece, "draw_ops");
1433 if (draw_ops_name != null) {
1434 draw_ops = getNode("draw_ops", new String[] { "name", draw_ops_name });
1435 } else {
1436 draw_ops = getNode(piece, "draw_ops", null);
1437 }
1438 variables.put("width", width);
1439 variables.put("height", height);
1440 g.translate(x, y);
1441 draw(draw_ops, g, jif);
1442 g.translate(-x, -y);
1443 }
1444 }
1445
1446
1447 Insets getBorderInsets(SynthContext context, Insets insets) {
1448 updateFrameGeometry(context);
1449
1450 if (insets == null) {
1451 insets = new Insets(0, 0, 0, 0);
1452 }
1453 insets.top = ((Insets)frameGeometry.get("title_border")).top;
1454 insets.bottom = getInt("bottom_height");
1455 insets.left = getInt("left_width");
1456 insets.right = getInt("right_width");
1457 return insets;
1458 }
1459
1460
1461 private void updateFrameGeometry(SynthContext context) {
1462 this.context = context;
1463 JComponent comp = context.getComponent();
1464 JComponent titlePane = findChild(comp, "InternalFrame.northPane");
1465
1466 JInternalFrame jif = null;
1467 if (comp instanceof JInternalFrame) {
1468 jif = (JInternalFrame)comp;
1469 } else if (comp instanceof JInternalFrame.JDesktopIcon) {
1470 jif = ((JInternalFrame.JDesktopIcon)comp).getInternalFrame();
1471 } else {
1472 assert false : "component is not JInternalFrame or JInternalFrame.JDesktopIcon";
1473 return;
1474 }
1475
1476 if (frame_style_set == null) {
1477 Node window = getNode("window", new String[]{"type", "normal"});
1478
1479 if (window != null) {
1480 frame_style_set = getNode("frame_style_set",
1481 new String[] {"name", getStringAttr(window, "style_set")});
1482 }
1483
1484 if (frame_style_set == null) {
1485 frame_style_set = getNode("frame_style_set", new String[] {"name", "normal"});
1486 }
1487 }
1488
1489 if (frame_style_set != null) {
1490 Node frame = getNode(frame_style_set, "frame", new String[] {
1491 "focus", (jif.isSelected() ? "yes" : "no"),
1492 "state", (jif.isMaximum() ? "maximized" : "normal")
1493 });
1494
1495 if (frame != null) {
1496 Node frame_style = getNode("frame_style", new String[] {
1497 "name", getStringAttr(frame, "style")
1498 });
1499 if (frame_style != null) {
1500 Map gm = frameGeometries.get(getStringAttr(frame_style, "geometry"));
1501
1502 setFrameGeometry(titlePane, gm);
1503 }
1504 }
1505 }
1506 }
1507
1508
1509 protected static void logError(String themeName, Exception ex) {
1510 logError(themeName, ex.toString());
1511 }
1512
1513 protected static void logError(String themeName, String msg) {
1514 if (!errorLogged) {
1515 System.err.println("Exception in Metacity for theme \""+themeName+"\": "+msg);
1516 errorLogged = true;
1517 }
1518 }
1519
1520
1521
1522
1523
1524 protected static Document getXMLDoc(final URL xmlFile)
1525 throws IOException,
1526 ParserConfigurationException,
1527 SAXException {
1528 if (documentBuilder == null) {
1529 documentBuilder =
1530 DocumentBuilderFactory.newInstance().newDocumentBuilder();
1531 }
1532 InputStream inputStream =
1533 AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
1534 public InputStream run() {
1535 try {
1536 return new BufferedInputStream(xmlFile.openStream());
1537 } catch (IOException ex) {
1538 return null;
1539 }
1540 }
1541 });
1542
1543 Document doc = null;
1544 if (inputStream != null) {
1545 doc = documentBuilder.parse(inputStream);
1546 }
1547 return doc;
1548 }
1549
1550
1551 protected Node[] getNodesByName(Node parent, String name) {
1552 NodeList nodes = parent.getChildNodes();
1553 int n = nodes.getLength();
1554 ArrayList<Node> list = new ArrayList<Node>();
1555 for (int i=0; i < n; i++) {
1556 Node node = nodes.item(i);
1557 if (name.equals(node.getNodeName())) {
1558 list.add(node);
1559 }
1560 }
1561 return list.toArray(new Node[list.size()]);
1562 }
1563
1564
1565
1566 protected Node getNode(String tagName, String[] attrs) {
1567 NodeList nodes = xmlDoc.getElementsByTagName(tagName);
1568 return (nodes != null) ? getNode(nodes, tagName, attrs) : null;
1569 }
1570
1571 protected Node getNode(Node parent, String name, String[] attrs) {
1572 Node node = null;
1573 NodeList nodes = parent.getChildNodes();
1574 if (nodes != null) {
1575 node = getNode(nodes, name, attrs);
1576 }
1577 if (node == null) {
1578 String inheritFrom = getStringAttr(parent, "parent");
1579 if (inheritFrom != null) {
1580 Node inheritFromNode = getNode(parent.getParentNode(),
1581 parent.getNodeName(),
1582 new String[] { "name", inheritFrom });
1583 if (inheritFromNode != null) {
1584 node = getNode(inheritFromNode, name, attrs);
1585 }
1586 }
1587 }
1588 return node;
1589 }
1590
1591 protected Node getNode(NodeList nodes, String name, String[] attrs) {
1592 int n = nodes.getLength();
1593 for (int i=0; i < n; i++) {
1594 Node node = nodes.item(i);
1595 if (name.equals(node.getNodeName())) {
1596 if (attrs != null) {
1597 NamedNodeMap nodeAttrs = node.getAttributes();
1598 if (nodeAttrs != null) {
1599 boolean matches = true;
1600 int nAttrs = attrs.length / 2;
1601 for (int a = 0; a < nAttrs; a++) {
1602 String aName = attrs[a * 2];
1603 String aValue = attrs[a * 2 + 1];
1604 Node attr = nodeAttrs.getNamedItem(aName);
1605 if (attr == null ||
1606 aValue != null && !aValue.equals(attr.getNodeValue())) {
1607 matches = false;
1608 break;
1609 }
1610 }
1611 if (matches) {
1612 return node;
1613 }
1614 }
1615 } else {
1616 return node;
1617 }
1618 }
1619 }
1620 return null;
1621 }
1622
1623 protected String getStringAttr(Node node, String name) {
1624 String value = null;
1625 NamedNodeMap attrs = node.getAttributes();
1626 if (attrs != null) {
1627 value = getStringAttr(attrs, name);
1628 if (value == null) {
1629 String inheritFrom = getStringAttr(attrs, "parent");
1630 if (inheritFrom != null) {
1631 Node inheritFromNode = getNode(node.getParentNode(),
1632 node.getNodeName(),
1633 new String[] { "name", inheritFrom });
1634 if (inheritFromNode != null) {
1635 value = getStringAttr(inheritFromNode, name);
1636 }
1637 }
1638 }
1639 }
1640 return value;
1641 }
1642
1643 protected String getStringAttr(NamedNodeMap attrs, String name) {
1644 Node item = attrs.getNamedItem(name);
1645 return (item != null) ? item.getNodeValue() : null;
1646 }
1647
1648 protected boolean getBooleanAttr(Node node, String name, boolean fallback) {
1649 String str = getStringAttr(node, name);
1650 if (str != null) {
1651 return Boolean.valueOf(str).booleanValue();
1652 }
1653 return fallback;
1654 }
1655
1656 protected int getIntAttr(Node node, String name, int fallback) {
1657 String str = getStringAttr(node, name);
1658 int value = fallback;
1659 if (str != null) {
1660 try {
1661 value = Integer.parseInt(str);
1662 } catch (NumberFormatException ex) {
1663 logError(themeName, ex);
1664 }
1665 }
1666 return value;
1667 }
1668
1669 protected float getFloatAttr(Node node, String name, float fallback) {
1670 String str = getStringAttr(node, name);
1671 float value = fallback;
1672 if (str != null) {
1673 try {
1674 value = Float.parseFloat(str);
1675 } catch (NumberFormatException ex) {
1676 logError(themeName, ex);
1677 }
1678 }
1679 return value;
1680 }
1681
1682
1683
1684 protected Color parseColor(String str) {
1685 StringTokenizer tokenizer = new StringTokenizer(str, "/");
1686 int n = tokenizer.countTokens();
1687 if (n > 1) {
1688 String function = tokenizer.nextToken();
1689 if ("shade".equals(function)) {
1690 assert (n == 3);
1691 Color c = parseColor2(tokenizer.nextToken());
1692 float alpha = Float.parseFloat(tokenizer.nextToken());
1693 return GTKColorType.adjustColor(c, 1.0F, alpha, alpha);
1694 } else if ("blend".equals(function)) {
1695 assert (n == 4);
1696 Color bg = parseColor2(tokenizer.nextToken());
1697 Color fg = parseColor2(tokenizer.nextToken());
1698 float alpha = Float.parseFloat(tokenizer.nextToken());
1699 if (alpha > 1.0f) {
1700 alpha = 1.0f / alpha;
1701 }
1702
1703 return new Color((int)(bg.getRed() + ((fg.getRed() - bg.getRed()) * alpha)),
1704 (int)(bg.getRed() + ((fg.getRed() - bg.getRed()) * alpha)),
1705 (int)(bg.getRed() + ((fg.getRed() - bg.getRed()) * alpha)));
1706 } else {
1707 System.err.println("Unknown Metacity color function="+str);
1708 return null;
1709 }
1710 } else {
1711 return parseColor2(str);
1712 }
1713 }
1714
1715 protected Color parseColor2(String str) {
1716 Color c = null;
1717 if (str.startsWith("gtk:")) {
1718 int i1 = str.indexOf('[');
1719 if (i1 > 3) {
1720 String typeStr = str.substring(4, i1).toLowerCase();
1721 int i2 = str.indexOf(']');
1722 if (i2 > i1+1) {
1723 String stateStr = str.substring(i1+1, i2).toUpperCase();
1724 int state = -1;
1725 if ("ACTIVE".equals(stateStr)) {
1726 state = PRESSED;
1727 } else if ("INSENSITIVE".equals(stateStr)) {
1728 state = DISABLED;
1729 } else if ("NORMAL".equals(stateStr)) {
1730 state = ENABLED;
1731 } else if ("PRELIGHT".equals(stateStr)) {
1732 state = MOUSE_OVER;
1733 } else if ("SELECTED".equals(stateStr)) {
1734 state = SELECTED;
1735 }
1736 ColorType type = null;
1737 if ("fg".equals(typeStr)) {
1738 type = GTKColorType.FOREGROUND;
1739 } else if ("bg".equals(typeStr)) {
1740 type = GTKColorType.BACKGROUND;
1741 } else if ("base".equals(typeStr)) {
1742 type = GTKColorType.TEXT_BACKGROUND;
1743 } else if ("text".equals(typeStr)) {
1744 type = GTKColorType.TEXT_FOREGROUND;
1745 } else if ("dark".equals(typeStr)) {
1746 type = GTKColorType.DARK;
1747 } else if ("light".equals(typeStr)) {
1748 type = GTKColorType.LIGHT;
1749 }
1750 if (state >= 0 && type != null) {
1751 c = ((GTKStyle)context.getStyle()).getGTKColor(context, state, type);
1752 }
1753 }
1754 }
1755 }
1756 if (c == null) {
1757 c = parseColorString(str);
1758 }
1759 return c;
1760 }
1761
1762 private static Color parseColorString(String str) {
1763 if (str.charAt(0) == '#') {
1764 str = str.substring(1);
1765
1766 int i = str.length();
1767
1768 if (i < 3 || i > 12 || (i % 3) != 0) {
1769 return null;
1770 }
1771
1772 i /= 3;
1773
1774 int r;
1775 int g;
1776 int b;
1777
1778 try {
1779 r = Integer.parseInt(str.substring(0, i), 16);
1780 g = Integer.parseInt(str.substring(i, i * 2), 16);
1781 b = Integer.parseInt(str.substring(i * 2, i * 3), 16);
1782 } catch (NumberFormatException nfe) {
1783 return null;
1784 }
1785
1786 if (i == 4) {
1787 return new ColorUIResource(r / 65535.0f, g / 65535.0f, b / 65535.0f);
1788 } else if (i == 1) {
1789 return new ColorUIResource(r / 15.0f, g / 15.0f, b / 15.0f);
1790 } else if (i == 2) {
1791 return new ColorUIResource(r, g, b);
1792 } else {
1793 return new ColorUIResource(r / 4095.0f, g / 4095.0f, b / 4095.0f);
1794 }
1795 } else {
1796 return XColors.lookupColor(str);
1797 }
1798 }
1799
1800 class ArithmeticExpressionEvaluator {
1801 private PeekableStringTokenizer tokenizer;
1802
1803 int evaluate(String expr) {
1804 tokenizer = new PeekableStringTokenizer(expr, " \t+-*/%()", true);
1805 return Math.round(expression());
1806 }
1807
1808 int evaluate(String expr, int fallback) {
1809 return (expr != null) ? evaluate(expr) : fallback;
1810 }
1811
1812 public float expression() {
1813 float value = getTermValue();
1814 boolean done = false;
1815 while (!done && tokenizer.hasMoreTokens()) {
1816 String next = tokenizer.peek();
1817 if ("+".equals(next) ||
1818 "-".equals(next) ||
1819 "`max`".equals(next) ||
1820 "`min`".equals(next)) {
1821 tokenizer.nextToken();
1822 float value2 = getTermValue();
1823 if ("+".equals(next)) {
1824 value += value2;
1825 } else if ("-".equals(next)) {
1826 value -= value2;
1827 } else if ("`max`".equals(next)) {
1828 value = Math.max(value, value2);
1829 } else if ("`min`".equals(next)) {
1830 value = Math.min(value, value2);
1831 }
1832 } else {
1833 done = true;
1834 }
1835 }
1836 return value;
1837 }
1838
1839 public float getTermValue() {
1840 float value = getFactorValue();
1841 boolean done = false;
1842 while (!done && tokenizer.hasMoreTokens()) {
1843 String next = tokenizer.peek();
1844 if ("*".equals(next) || "/".equals(next) || "%".equals(next)) {
1845 tokenizer.nextToken();
1846 float value2 = getFactorValue();
1847 if ("*".equals(next)) {
1848 value *= value2;
1849 } else if ("/".equals(next)) {
1850 value /= value2;
1851 } else {
1852 value %= value2;
1853 }
1854 } else {
1855 done = true;
1856 }
1857 }
1858 return value;
1859 }
1860
1861 public float getFactorValue() {
1862 float value;
1863 if ("(".equals(tokenizer.peek())) {
1864 tokenizer.nextToken();
1865 value = expression();
1866 tokenizer.nextToken();
1867 } else {
1868 String token = tokenizer.nextToken();
1869 if (Character.isDigit(token.charAt(0))) {
1870 value = Float.parseFloat(token);
1871 } else {
1872 Integer i = variables.get(token);
1873 if (i == null) {
1874 i = (Integer)getFrameGeometry().get(token);
1875 }
1876 if (i == null) {
1877 logError(themeName, "Variable \"" + token + "\" not defined");
1878 return 0;
1879 }
1880 value = (i != null) ? i.intValue() : 0F;
1881 }
1882 }
1883 return value;
1884 }
1885
1886
1887 }
1888
1889 static class PeekableStringTokenizer extends StringTokenizer {
1890 String token = null;
1891
1892 public PeekableStringTokenizer(String str, String delim,
1893 boolean returnDelims) {
1894 super(str, delim, returnDelims);
1895 peek();
1896 }
1897
1898 public String peek() {
1899 if (token == null) {
1900 token = nextToken();
1901 }
1902 return token;
1903 }
1904
1905 public boolean hasMoreTokens() {
1906 return (token != null || super.hasMoreTokens());
1907 }
1908
1909 public String nextToken() {
1910 if (token != null) {
1911 String t = token;
1912 token = null;
1913 if (hasMoreTokens()) {
1914 peek();
1915 }
1916 return t;
1917 } else {
1918 String token = super.nextToken();
1919 while ((token.equals(" ") || token.equals("\t"))
1920 && hasMoreTokens()) {
1921 token = super.nextToken();
1922 }
1923 return token;
1924 }
1925 }
1926 }
1927
1928
1929 static class RoundRectClipShape extends RectangularShape {
1930 static final int TOP_LEFT = 1;
1931 static final int TOP_RIGHT = 2;
1932 static final int BOTTOM_LEFT = 4;
1933 static final int BOTTOM_RIGHT = 8;
1934
1935 int x;
1936 int y;
1937 int width;
1938 int height;
1939 int arcwidth;
1940 int archeight;
1941 int corners;
1942
1943 public RoundRectClipShape() {
1944 }
1945
1946 public RoundRectClipShape(int x, int y, int w, int h,
1947 int arcw, int arch, int corners) {
1948 setRoundedRect(x, y, w, h, arcw, arch, corners);
1949 }
1950
1951 public void setRoundedRect(int x, int y, int w, int h,
1952 int arcw, int arch, int corners) {
1953 this.corners = corners;
1954 this.x = x;
1955 this.y = y;
1956 this.width = w;
1957 this.height = h;
1958 this.arcwidth = arcw;
1959 this.archeight = arch;
1960 }
1961
1962 public double getX() {
1963 return (double)x;
1964 }
1965
1966 public double getY() {
1967 return (double)y;
1968 }
1969
1970 public double getWidth() {
1971 return (double)width;
1972 }
1973
1974 public double getHeight() {
1975 return (double)height;
1976 }
1977
1978 public double getArcWidth() {
1979 return (double)arcwidth;
1980 }
1981
1982 public double getArcHeight() {
1983 return (double)archeight;
1984 }
1985
1986 public boolean isEmpty() {
1987 return false;
1988 }
1989
1990 public Rectangle2D getBounds2D() {
1991 return null;
1992 }
1993
1994 public int getCornerFlags() {
1995 return corners;
1996 }
1997
1998 public void setFrame(double x, double y, double w, double h) {
1999
2000 }
2001
2002 public boolean contains(double x, double y) {
2003 return false;
2004 }
2005
2006 private int classify(double coord, double left, double right, double arcsize) {
2007 return 0;
2008 }
2009
2010 public boolean intersects(double x, double y, double w, double h) {
2011 return false;
2012 }
2013
2014 public boolean contains(double x, double y, double w, double h) {
2015 return false;
2016 }
2017
2018 public PathIterator getPathIterator(AffineTransform at) {
2019 return new RoundishRectIterator(this, at);
2020 }
2021
2022
2023 static class RoundishRectIterator implements PathIterator {
2024 double x, y, w, h, aw, ah;
2025 AffineTransform affine;
2026 int index;
2027
2028 double ctrlpts[][];
2029 int types[];
2030
2031 private static final double angle = Math.PI / 4.0;
2032 private static final double a = 1.0 - Math.cos(angle);
2033 private static final double b = Math.tan(angle);
2034 private static final double c = Math.sqrt(1.0 + b * b) - 1 + a;
2035 private static final double cv = 4.0 / 3.0 * a * b / c;
2036 private static final double acv = (1.0 - cv) / 2.0;
2037
2038
2039
2040
2041
2042 private static final double CtrlPtTemplate[][] = {
2043 { 0.0, 0.0, 1.0, 0.0 },
2044 { 0.0, 0.0, 1.0, -0.5 },
2045 { 0.0, 0.0, 1.0, -acv,
2046 0.0, acv, 1.0, 0.0,
2047 0.0, 0.5, 1.0, 0.0 },
2048 { 1.0, 0.0, 1.0, 0.0 },
2049 { 1.0, -0.5, 1.0, 0.0 },
2050 { 1.0, -acv, 1.0, 0.0,
2051 1.0, 0.0, 1.0, -acv,
2052 1.0, 0.0, 1.0, -0.5 },
2053 { 1.0, 0.0, 0.0, 0.0 },
2054 { 1.0, 0.0, 0.0, 0.5 },
2055 { 1.0, 0.0, 0.0, acv,
2056 1.0, -acv, 0.0, 0.0,
2057 1.0, -0.5, 0.0, 0.0 },
2058 { 0.0, 0.0, 0.0, 0.0 },
2059 { 0.0, 0.5, 0.0, 0.0 },
2060 { 0.0, acv, 0.0, 0.0,
2061 0.0, 0.0, 0.0, acv,
2062 0.0, 0.0, 0.0, 0.5 },
2063 {},
2064 };
2065 private static final int CornerFlags[] = {
2066 RoundRectClipShape.BOTTOM_LEFT,
2067 RoundRectClipShape.BOTTOM_RIGHT,
2068 RoundRectClipShape.TOP_RIGHT,
2069 RoundRectClipShape.TOP_LEFT,
2070 };
2071
2072 RoundishRectIterator(RoundRectClipShape rr, AffineTransform at) {
2073 this.x = rr.getX();
2074 this.y = rr.getY();
2075 this.w = rr.getWidth();
2076 this.h = rr.getHeight();
2077 this.aw = Math.min(w, Math.abs(rr.getArcWidth()));
2078 this.ah = Math.min(h, Math.abs(rr.getArcHeight()));
2079 this.affine = at;
2080 if (w < 0 || h < 0) {
2081
2082 ctrlpts = new double[0][];
2083 types = new int[0];
2084 } else {
2085 int corners = rr.getCornerFlags();
2086 int numedges = 5;
2087 for (int i = 1; i < 0x10; i <<= 1) {
2088
2089 if ((corners & i) != 0) numedges++;
2090 }
2091 ctrlpts = new double[numedges][];
2092 types = new int[numedges];
2093 int j = 0;
2094 for (int i = 0; i < 4; i++) {
2095 types[j] = SEG_LINETO;
2096 if ((corners & CornerFlags[i]) == 0) {
2097 ctrlpts[j++] = CtrlPtTemplate[i*3+0];
2098 } else {
2099 ctrlpts[j++] = CtrlPtTemplate[i*3+1];
2100 types[j] = SEG_CUBICTO;
2101 ctrlpts[j++] = CtrlPtTemplate[i*3+2];
2102 }
2103 }
2104 types[j] = SEG_CLOSE;
2105 ctrlpts[j++] = CtrlPtTemplate[12];
2106 types[0] = SEG_MOVETO;
2107 }
2108 }
2109
2110 public int getWindingRule() {
2111 return WIND_NON_ZERO;
2112 }
2113
2114 public boolean isDone() {
2115 return index >= ctrlpts.length;
2116 }
2117
2118 public void next() {
2119 index++;
2120 }
2121
2122 public int currentSegment(float[] coords) {
2123 if (isDone()) {
2124 throw new NoSuchElementException("roundrect iterator out of bounds");
2125 }
2126 double ctrls[] = ctrlpts[index];
2127 int nc = 0;
2128 for (int i = 0; i < ctrls.length; i += 4) {
2129 coords[nc++] = (float) (x + ctrls[i + 0] * w + ctrls[i + 1] * aw);
2130 coords[nc++] = (float) (y + ctrls[i + 2] * h + ctrls[i + 3] * ah);
2131 }
2132 if (affine != null) {
2133 affine.transform(coords, 0, coords, 0, nc / 2);
2134 }
2135 return types[index];
2136 }
2137
2138 public int currentSegment(double[] coords) {
2139 if (isDone()) {
2140 throw new NoSuchElementException("roundrect iterator out of bounds");
2141 }
2142 double ctrls[] = ctrlpts[index];
2143 int nc = 0;
2144 for (int i = 0; i < ctrls.length; i += 4) {
2145 coords[nc++] = x + ctrls[i + 0] * w + ctrls[i + 1] * aw;
2146 coords[nc++] = y + ctrls[i + 2] * h + ctrls[i + 3] * ah;
2147 }
2148 if (affine != null) {
2149 affine.transform(coords, 0, coords, 0, nc / 2);
2150 }
2151 return types[index];
2152 }
2153 }
2154 }
2155 }